package production

import (
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	sc "github.com/hyperledger/fabric/protos/peer"
	"strconv"
	"strings"
)

type ProductUnit struct {
	BatchInfo UnitInfo `json:"batch_info"`
	UnitNos   []string `json:"unit_nos"`
}

type UnitInfo struct {
	ProductTime   string `json:"product_time"`
	ItemNo        string `json:"item_no"`
	EnterBatch    string `json:"enter_batch"`
	Producer      string `json:"producer"`
	Environment   string `json:"environment"`
	MaterialBatch string `json:"material_batch"`
	EnterTime     string `json:"enter_time"`
	NumOfSonPacks string `json:"num_of_son_packs"`
	PackLevel     string `json:"pack_level"`
}

type RepackageInfo struct {
	ItemInfo    UnitInfo  `json:"item_info"`
	QueriedInfo QueryInfo `json:"queried_info"`
}

type QRInfo struct {
	ItemInfo     UnitInfo    `json:"item_info"`
	QueryHistory []QueryInfo `json:"query_history"`
}

type QueryInfo struct {
	AddressInfo    Address `json:"address_info"`
	QueryTime      string  `json:"query_time"`
	BeQueriedTimes int     `json:"be_queried_times"`
	UserAgent      string  `json:"user_agent"`
	AppUserId      string  `json:"app_user_id"`
}

type Address struct {
	AddressDetail string    `json:"address_detail"`
	Coordinates   []float32 `json:"coordinates"`
	IpAddress     string    `json:"ip_address"`
}

func (p ProductUnit) SaveProductUnitInfo(APIstub shim.ChaincodeStubInterface, batchKey string) sc.Response {
	finalBatchKey, err := APIstub.CreateCompositeKey("BatchForProduct", []string{string([]byte(batchKey)[0:39]), batchKey})
	if err != nil {
		return shim.Error("系统错误。")
	}
	value, err := json.Marshal(p)
	if err != nil {
		return shim.Error("系统错误。")
	}
	err = APIstub.PutState(finalBatchKey, value)
	if err != nil {
		return shim.Error("系统错误。")
	}
	return shim.Success(nil)
}

func (p *ProductUnit) QueryProductUnitInfo(APIstub shim.ChaincodeStubInterface, itemNo string) error {
	valueIterator, err := APIstub.GetStateByPartialCompositeKey("BatchForProduct", []string{string([]byte(itemNo)[0:39])})
	if err != nil {
		return err
	} else if valueIterator == nil {
		return fmt.Errorf("找不到您查询的商品。")
	}
	defer valueIterator.Close()

	flag := false
	for valueIterator.HasNext() {
		value, err := valueIterator.Next()
		if err != nil {
			return err
		}

		var pt ProductUnit

		err = json.Unmarshal(value.Value, &pt)
		if err != nil {
			return err
		}

		if pt.checkItemsNo(itemNo) {
			flag = true
			*p = pt
			break
		}
	}
	if !flag {
		return fmt.Errorf("找不到您查询的商品。")
	}
	return nil
}

func (p ProductUnit) checkItemsNo(itemNo string) bool {
	flag := false
	for _, i := range p.UnitNos {
		if i == itemNo {
			flag = true
		}
	}
	return flag
}

func (p ProductUnit) Repackage(APIstub shim.ChaincodeStubInterface, address string, queryTime string, itemNo string, userAgent string, longitude string, latitude string, ipAddress string, appUserId string) error {
	longitudeFloat, err := strconv.ParseFloat(longitude, 32)
	latitudeFloat, err := strconv.ParseFloat(latitude, 32)
	if err != nil {
		return err
	}

	repackageInfo := RepackageInfo{
		p.BatchInfo,
		QueryInfo{
			Address{
				address,
				[]float32{float32(longitudeFloat), float32(latitudeFloat)},
				ipAddress,
			},
			queryTime,
			1,
			userAgent,
			appUserId,
		},
	}

	value, err := json.Marshal(repackageInfo)
	if err != nil {
		return err
	}
	err = APIstub.PutState(itemNo, value)
	if err != nil {
		return err
	}

	return nil
}

func (q *QRInfo) QueryByQR(APIstub shim.ChaincodeStubInterface, qR string, value []byte, address string, ipAddress string, userAgent string, appUserId string) (bool, error) {
	//value, err := APIstub.GetState(qR)
	//if err != nil {
	//	return err
	//} else if value == nil {
	//	return fmt.Errorf("找不到您查询的商品。")
	//}
	queriedFlag := false
	var repackageInfo RepackageInfo
	err := json.Unmarshal(value, &repackageInfo)
	q.ItemInfo = repackageInfo.ItemInfo

	iterator, err := APIstub.GetHistoryForKey(qR)
	if err != nil {
		return queriedFlag, err
	}
	defer iterator.Close()

	var history []QueryInfo
	for iterator.HasNext() {
		response, err := iterator.Next()
		if err != nil {
			return queriedFlag, err
		}
		var r RepackageInfo
		err = json.Unmarshal(response.Value, &r)
		if err != nil {
			return queriedFlag, err
		}
		history = append(history, r.QueriedInfo)
		if appUserId == "0" {
			if (getDeviceTypeFromUA(r.QueriedInfo.UserAgent) == getDeviceTypeFromUA(userAgent)) && (r.QueriedInfo.AddressInfo.AddressDetail == address) && (r.QueriedInfo.AddressInfo.IpAddress == ipAddress) {
				queriedFlag = true
			}
		} else {
			if r.QueriedInfo.AppUserId == appUserId {
				queriedFlag = true
			}
		}

	}
	q.QueryHistory = history
	return queriedFlag, nil
}

func (r *RepackageInfo) UpdateQueryInfo(APIstub shim.ChaincodeStubInterface, address string, queryTime string, qR string, userAgent string, longitude string, latitude string, ipAddress string, appUserId string) error {
	longitudeFloat, err := strconv.ParseFloat(longitude, 32)
	latitudeFloat, err := strconv.ParseFloat(latitude, 32)
	if err != nil {
		return err
	}

	value, err := APIstub.GetState(qR)
	if err != nil {
		return err
	} else if value == nil {
		return fmt.Errorf("找不到您查询的商品。")
	}

	err = json.Unmarshal(value, r)
	r.QueriedInfo.AddressInfo.AddressDetail = address
	r.QueriedInfo.AddressInfo.Coordinates = []float32{float32(longitudeFloat), float32(latitudeFloat)}
	r.QueriedInfo.AddressInfo.IpAddress = ipAddress
	r.QueriedInfo.QueryTime = queryTime
	r.QueriedInfo.BeQueriedTimes += 1
	r.QueriedInfo.UserAgent = userAgent
	r.QueriedInfo.AppUserId = appUserId

	value, err = json.Marshal(r)
	if err != nil {
		return err
	}
	err = APIstub.PutState(qR, value)
	if err != nil {
		return err
	}

	return nil
}

func getDeviceTypeFromUA(ua string) string {
	mozillaInfoStartIndex := strings.Index(ua, "(")
	mozillaInfoEndIndex := strings.Index(ua, ")")
	mozillaInfo := string([]byte(ua)[mozillaInfoStartIndex+1 : mozillaInfoEndIndex])
	mozillaInfos := strings.Split(mozillaInfo, "; ")

	for _, m := range mozillaInfos {
		if strings.Contains(m, "Build") {
			return m
		}
		if strings.Contains(m, "Mac") {
			return m
		}
		if strings.Contains(m, "Nokia") {
			return m
		}
	}
	return ""
}
